<script setup>
import { markRaw, onMounted, ref, watch } from 'vue'
import Input from './components/Input.vue'
import Autocomplete from './components/Autocomplete.vue'
import Radio from './components/Radio.vue'
import Checkbox from './components/Checkbox.vue'
import InputNumber from './components/InputNumber.vue'
import Switch from './components/Switch.vue'
import Select from './components/Select.vue'
import Cascader from './components/Cascader.vue'
import Slider from './components/Slider.vue'
import Rate from './components/Rate.vue'
import Transfer from './components/Transfer.vue'
import TimePicker from './components/TimePicker.vue'
import DatePicker from './components/DatePicker.vue'
import Upload from './components/Upload.vue'

const componentsTypes = markRaw({
  Input,
  Autocomplete,
  Radio,
  Checkbox,
  InputNumber,
  Switch,
  Select,
  Cascader,
  Slider,
  Rate,
  Transfer,
  TimePicker,
  DatePicker,
  Upload
})

/**
 * 传入的props数据类型
 * formData：form表单的数据值
 *    表单某一项没有初始值的情况下，可以不用传，配置项当中的prop将是属性名
 * formColumn：表单的配置项
 *    layout：el-col接受的props
 *    formItemOpt: el-form-item接受的props
 *    comOpts: 生成组件需要的配置
 *        xType: 渲染的元素类型
 *        prop： el-form-item需要的prop，确定元素绑定formData哪一条数据
 *        refName: 组件的ref名称，默认由prop + 固定的 'Ref' 组成
 *        其它的各元素属性照着element-ui配置
 *        插槽按照ui文档的插槽名传入字符串或者h创建的虚拟函数
 *        事件按照ui文档的事件(Events)名传入对应的函数
 *        传入事件 getFIRefs 获取每个组件的ref 可以调用组件的Methods（见ui文档）(部分组件是没有methods的，所以不会返回ref)
 * formAttr：el-form接受的props
 * rowLayout：el-row接受的props
 */

const props = defineProps({
  formData: {
    default: null
  },
  formColumn: Array,
  formRules: Object,
  formAttr: {
    type: Object,
    default: () => ({
      labelWidth: '80px'
    })
  },
  rowLayout: {
    type: Object,
    default: () => ({
      gutter: 20
    })
  }
})

const FD = ref(props.formData)
watch(() => props.formData, newVal => {
  FD.value = newVal
})

const emit = defineEmits(['getFRef', 'getFIRefs'])

// 获取各组件的ref
const formRefs = ref({})
const getRef = refs => {
  const { name, value } = refs
  formRefs.value[name] = value
  emit('getFIRefs', formRefs.value)
}

const formRef = ref(null)
onMounted(() => {
  emit('getFRef', formRef.value)
})

defineExpose({
  formRef
})
</script>

<template>
  <el-form 
    ref="formRef"
    :model="FD" 
    v-bind="formAttr"
    :rules="formRules"
    @validate="formAttr.validate || (() => {})">
    <el-row 
      v-bind="rowLayout">
      <el-col 
        v-for="(elem, index) in formColumn" 
        :key="index + (elem.prop || '')" 
        v-bind="elem.layout">
        <el-form-item 
          :label="elem.comOpts.label" 
          :prop="elem.comOpts.prop" 
          v-bind="elem.formItemOpt">
          <component
            v-if="elem.comOpts.xType === 'Upload'"
            :is="componentsTypes[elem.comOpts.xType]"
            :file-list.sync="FD[elem.comOpts.prop]"
            v-bind="elem.comOpts"
            @getRef="getRef"
          />
          <component
            v-else
            :is="componentsTypes[elem.comOpts.xType]"
            v-model="FD[elem.comOpts.prop]"
            v-bind="elem.comOpts"
            :placeholder="elem.comOpts.placeholder || (['Cascader', 'DatePicker', 'Select', 'TimePicker'].includes(elem.comOpts.xType) ? `请选择 ${elem.comOpts.label}` : `请输入 ${elem.comOpts.label}`)"
            @getRef="getRef"
          />
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
